/*
 * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC
 *
 * Copyright (C) 2009 Bluewater Systems Ltd
 *
 * Author: Ryan Mallon <ryan@bluewatersys.com>
 *
 * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/swab.h>
#include <linux/i2c.h>
#include <linux/idr.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/ds2782_battery.h>

#define DS2782_REG_AS		0x14
#define DS2782_REG_ACR		0x10


#define DS2782_REG_RARC		0x06	/* Remaining active relative capacity */

#define DS278x_REG_VOLT_MSB	0x0c
#define DS278x_REG_TEMP_MSB	0x0a
#define DS278x_REG_CURRENT_MSB	0x0e

/* EEPROM Block */
#define DS2782_REG_RSNSP	0x69	/* Sense resistor value */

/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
#define DS2782_CURRENT_UNITS	1563

#define DS2786_REG_RARC		0x02	/* Remaining active relative capacity */

#define DS2786_CURRENT_UNITS	25

struct ds278x_info;

struct ds278x_battery_ops {
	int (*get_battery_current)(struct ds278x_info *info, int *current_uA);
	int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uA);
	int (*get_battery_capacity)(struct ds278x_info *info, int *capacity_uA);
	int (*init_reg)(struct ds278x_info *info);
};

#define to_ds278x_info(x) container_of(x, struct ds278x_info, battery)

struct ds278x_info {
	struct i2c_client	*client;
	struct power_supply	battery;
	struct ds278x_battery_ops  *ops;
	int			id;
	int                     rsns;
};

static DEFINE_IDR(battery_id);
static DEFINE_MUTEX(battery_lock);

static inline int ds278x_write_byte(struct ds278x_info *info, int reg, u8 val)
{
	unsigned char tbuf[2] = {reg, val};
	struct i2c_client *client = info->client;
	int k = 0;

	struct i2c_msg msg[] = {
		{ client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
	};

	int count = sizeof(msg) / sizeof(struct i2c_msg);
	/* read registers */
	if ((i2c_transfer(client->adapter, msg, count)) != count) {
		dev_err(&client->dev, "%s error\n", __func__);
		return -EIO;
	}
	return 0;
}
static inline int ds278x_write_byte16(struct ds278x_info *info, int reg, u16 val)
{
	unsigned char tbuf[3] = {reg, val >> 8, val & 0xff};
	struct i2c_client *client = info->client;
	int k = 0;

	struct i2c_msg msg[] = {
		{ client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
	};

	int count = sizeof(msg) / sizeof(struct i2c_msg);
	/* read registers */
	if ((i2c_transfer(client->adapter, msg, count)) != count) {
		dev_err(&client->dev, "%s error\n", __func__);
		return -EIO;
	}
	return 0;
}
static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val)
{
	/*
	int ret;

	ret = i2c_smbus_read_byte_data(info->client, reg);
	if (ret < 0) {
		dev_err(&info->client->dev, "register read failed\n");
		return ret;
	}

	*val = ret;
	*/
	// reg = 0x01;
	// printk("line %d reg 0x%x\r\n", __LINE__, reg);
	unsigned char rbuf[1] = {0};
	unsigned char tbuf[1] = {reg};
	struct i2c_client *client = info->client;
	int k = 0;
	// printk("addr %x\r\n", client->addr);
	struct i2c_msg msg[] = {
		{ client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
		{ client->addr, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
		//		{0x34, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
	};

	int count = sizeof(msg) / sizeof(struct i2c_msg);
	/* read registers */
	if ((i2c_transfer(client->adapter, msg, count)) != count) {
		dev_err(&client->dev, "%s: read error\n", __func__);
		return -EIO;
	}
	int i;
	//printk("page 0x%2.2x(%d)\r\n", page, addr_in_page);
	// for (i = 0; i < sizeof(rbuf); i++) {
	// 	printk("%2.2x ", rbuf[i]);
	// }
	// printk("\r\n");
	*val = rbuf[0];
	return 0;
}

static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
                                    s16 *val)
{
	// printk("line %d 0x[%x]\r\n", __LINE__, reg_msb);
	int ret;
	char bL, bH;
	ds278x_read_reg(info, reg_msb, &bH);
	ds278x_read_reg(info, reg_msb + 1, &bL);
	//	ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb));
	ret = (bH << 8) | bL;
	if (ret < 0) {
		dev_err(&info->client->dev, "register read failed\n");
		return ret;
	}
	// printk("ret = %x\r\n", ret);
	*val = ret;
	return 0;
}

static int ds278x_get_temp(struct ds278x_info *info, int *temp)
{
	s16 raw;
	int err;

	/*
	 * Temperature is measured in units of 0.125 degrees celcius, the
	 * power_supply class measures temperature in tenths of degrees
	 * celsius. The temperature value is stored as a 10 bit number, plus
	 * sign in the upper bits of a 16 bit register.
	 */
	err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw);
	if (err) {
		return err;
	}
	*temp = ((raw / 32) * 125) / 1000;
	return 0;
}
void change(struct ds278x_info *info)
{

	printk("dddddddddddd111111---aaa---1\r\n");
	// ds278x_write_byte(info, 0x64, 188);  //  VCHG
	// ds278x_write_byte(info, 0x65, 120);  //  IMIN
	// ds278x_write_byte(info, 0x66, 158);  // VAE
	// ds278x_write_byte(info, 0x67, 57);   // IAE


	// ds278x_write_byte(info, 0x10, 0x08);   // ACR
	// ds278x_write_byte(info, 0x11, 0x11);   // ACR

	// ds278x_write_byte(info, 0x10, 0x30);   // ACR
	// ds278x_write_byte(info, 0x11, 0x20);   // ACR
	///////////////
ds278x_write_byte(info, 0x61, 0x04);
ds278x_write_byte(info, 0x62, 0x30);
ds278x_write_byte(info, 0x63, 0x20);
ds278x_write_byte(info, 0x64, 0xc3);
ds278x_write_byte(info, 0x65, 0xa0);
ds278x_write_byte(info, 0x66, 0xb8);
ds278x_write_byte(info, 0x67, 0x32);
ds278x_write_byte(info, 0x68, 0x01);
ds278x_write_byte(info, 0x69, 0x32);
ds278x_write_byte(info, 0x6a, 0x30);
ds278x_write_byte(info, 0x6b, 0x20);
ds278x_write_byte(info, 0x6c, 0x08);
ds278x_write_byte(info, 0x6d, 0x0c);
ds278x_write_byte(info, 0x6e, 0x08);
ds278x_write_byte(info, 0x6f, 0x0c);
ds278x_write_byte(info, 0x6c, 0x11);
ds278x_write_byte(info, 0x6d, 0x22);
ds278x_write_byte(info, 0x6e, 0x37);
ds278x_write_byte(info, 0x6f, 0x7b);
ds278x_write_byte(info, 0x74, 0x00);
ds278x_write_byte(info, 0x75, 0x01);
ds278x_write_byte(info, 0x76, 0x02);
ds278x_write_byte(info, 0x77, 0x07);
ds278x_write_byte(info, 0x78, 0x04);
ds278x_write_byte(info, 0x79, 0x1e);
ds278x_write_byte(info, 0x7a, 0x00);
}
static int ds2782_init_reg(struct ds278x_info *info)
{
	int err;
	int i;

#define RESET_REG (0)
#define CONFIG_REG (0)
	
#if RESET_REG
	printk("clear\r\n");
	ds278x_write_byte(info, DS2782_REG_AS, 0x60);
	ds278x_write_byte16(info, 0x10, 0xffff);   // ACR
#endif
	// for (i = 0x61; i < 0x7f; i++) {
	// 	err = ds278x_write_byte(info, i, 0);
	// }
#if CONFIG_REG
	change(info);
#endif
	return 0;
	

	
	// err = ds278x_write_byte(info, DS2782_REG_AS, 0x80);
	// err = ds278x_write_byte16(info, DS2782_REG_ACR, 0x6201);
	err = ds278x_write_byte16(info, DS2782_REG_ACR, 0);
	// err = ds278x_write_byte16(info, DS2782_REG_ACR, 0x0010);
	// err = ds278x_write_byte16(info, DS2782_REG_ACR + 1, 0x01);


	
	return 0;
	err = ds278x_write_byte(info, DS2782_REG_RSNSP, 0x32);

	err = ds278x_write_byte(info, 0x61, 0x04);
	err = ds278x_write_byte(info, 0x62, 0x0c);
	err = ds278x_write_byte(info, 0x63, 0x84);
	err = ds278x_write_byte(info, 0x64, 0xd5);
	err = ds278x_write_byte(info, 0x65, 0x20);

	err = ds278x_write_byte(info, 0x66, 0xbd);// VAE
	err = ds278x_write_byte(info, 0x67, 0x18);// IAE

	err = ds278x_write_byte(info, 0x68, 0x06);
	err = ds278x_write_byte(info, 0x69, 0x32);
	// err = ds278x_write_byte(info, 0x69, 20);



	////////////////////////////
	// return 0;
	err = ds278x_write_byte(info, 0x66, 200);
	err = ds278x_write_byte(info, 0x67, 160);

	err = ds278x_write_byte(info, 0x64, 0x7f);//  VCHG
	err = ds278x_write_byte(info, 0x65, 0x02);//  IMIN
	////////////////////////////

	// FULL 40
	err = ds278x_write_byte(info, 0x6a, 0x0c);
	err = ds278x_write_byte(info, 0x6b, 0xb0);
	// FULL
	err = ds278x_write_byte(info, 0x6c, 0x10);
	err = ds278x_write_byte(info, 0x6d, 0x17);
	err = ds278x_write_byte(info, 0x6e, 0x3c);
	err = ds278x_write_byte(info, 0x6f, 0x49);

	// AE
	err = ds278x_write_byte(info, 0x70, 0x06);
	err = ds278x_write_byte(info, 0x71, 0x0d);
	err = ds278x_write_byte(info, 0x72, 0x15);
	err = ds278x_write_byte(info, 0x73, 0x2f);

	// SE
	err = ds278x_write_byte(info, 0x74, 0x03);
	err = ds278x_write_byte(info, 0x75, 0x05);
	err = ds278x_write_byte(info, 0x76, 0x08);
	err = ds278x_write_byte(info, 0x77, 0x1b);


	err = ds278x_write_byte(info, 0x78, 0x04);
	err = ds278x_write_byte(info, 0x79, 0x1f);

	err = ds278x_write_byte(info, 0x74, 0x00);
	change(info);

}
static int ds2786_init_reg(struct ds278x_info *info)
{
	printk("未实现\r\n");
}
static int ds2782_get_current(struct ds278x_info *info, int *current_uA)
{
	int sense_res;
	int err;
	u8 sense_res_raw;
	s16 raw;
	int sns;
	int cur;
	float cur_uA;

	// err = ds278x_write_byte(info, DS2782_REG_AS, 0x80);
	// err = ds278x_write_byte(info, DS2782_REG_ACR, 0x66);
	// err = ds278x_write_byte(info, DS2782_REG_ACR + 1, 0x01);
	// err = ds278x_write_byte(info, DS2782_REG_AS, 0);
	// err = ds278x_write_byte(info, DS2782_REG_ACR, 0);
	// err = ds278x_write_byte(info, DS2782_REG_ACR + 1, 0);

	// err = ds278x_write_byte(info, DS2782_REG_RSNSP, 0x32);
	// if (err) {
	// 	printk("ds2782_get_current error\r\n");
	// 	return err;
	// }
	/*
	 * The units of measurement for current are dependent on the value of
	 * the sense resistor.
	 */
	err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
	printk("err = %d\r\n", err);
	if (sense_res_raw == 0) {
		ds278x_write_byte(info, DS2782_REG_RSNSP, 0x32);
	}
	err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
	if (err) {
		return err;
	}
	// printk("sense_res_raw %x\r\n", sense_res_raw);
	// sense_res_raw = 20;
	if (sense_res_raw == 0) {
		dev_err(&info->client->dev, "sense resistor value is 0\n");
		return -ENXIO;
	}
	sense_res = 1000 / sense_res_raw;  // 不能出现 float 否则会崩溃

	// printk(&info->client->dev, "sense resistor = %d milli-ohms\n",
	// 	sense_res);
	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
	if (err) {
		return err;
	}
	// printk("current %d 0x%x\r\n",  raw, raw);
	cur = raw;
	*current_uA = raw * (DS2782_CURRENT_UNITS / sense_res);

	return 0;
}

static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_mV)
{
	s16 raw;
	int err;

	/*
	 * Voltage is measured in units of 4.88mV. The voltage is stored as
	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
	 */
	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
	if (err) {
		return err;
	}
	// *voltage_uA = (raw / 32) * 4800;
	*voltage_mV = (raw / 32) * 488 / 100;
	return 0;
}

static int ds2782_get_capacity(struct ds278x_info *info, int *capacity)
{
#if 1
	int err;
	u8 raw;


	err = ds278x_read_reg(info, DS2782_REG_RARC, &raw);
	if (err) {
		return err;
	}
	*capacity = raw;
	return 0;
#endif
#if 0
	int err;
	s16 raw;
	int vol;

#define AE (2800)
#define FULL (3700)

	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
	if (err) {
		return err;
	}
	vol = (raw / 32) * 488 / 100;


	*capacity = (vol - AE) * 100 / (FULL - AE);
	return 0;
#endif
}

static int ds2786_get_current(struct ds278x_info *info, int *current_uA)
{
	int err;
	s16 raw;

	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
	if (err) {
		return err;
	}
	*current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns);
	return 0;
}

static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA)
{
	s16 raw;
	int err;

	/*
	 * Voltage is measured in units of 1.22mV. The voltage is stored as
	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
	 */
	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
	if (err) {
		return err;
	}
	*voltage_uA = (raw / 8) * 1220;
	return 0;
}

static int ds2786_get_capacity(struct ds278x_info *info, int *capacity)
{
	int err;
	u8 raw;

	err = ds278x_read_reg(info, DS2786_REG_RARC, &raw);
	if (err) {
		return err;
	}
	/* Relative capacity is displayed with resolution 0.5 % */
	*capacity = raw / 2 ;
	return 0;
}

static int ds278x_get_status(struct ds278x_info *info, int *status)
{
	int err;
	int current_uA;
	int capacity;

	err = info->ops->get_battery_current(info, &current_uA);
	if (err) {
		return err;
	}

	err = info->ops->get_battery_capacity(info, &capacity);
	if (err) {
		return err;
	}

	if (capacity == 100) {
		*status = POWER_SUPPLY_STATUS_FULL;
	} else if (current_uA == 0) {
		*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
	} else if (current_uA < 0) {
		*status = POWER_SUPPLY_STATUS_DISCHARGING;
	} else {
		*status = POWER_SUPPLY_STATUS_CHARGING;
	}

	return 0;
}

static int ds278x_battery_get_property(struct power_supply *psy,
                                       enum power_supply_property prop,
                                       union power_supply_propval *val)
{
	struct ds278x_info *info = to_ds278x_info(psy);
	int ret;

	switch (prop) {
	case POWER_SUPPLY_PROP_STATUS:
		ret = ds278x_get_status(info, &val->intval);
		break;

	case POWER_SUPPLY_PROP_CAPACITY:
		ret = info->ops->get_battery_capacity(info, &val->intval);
		break;

	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
		ret = info->ops->get_battery_voltage(info, &val->intval);
		break;

	case POWER_SUPPLY_PROP_CURRENT_NOW:
		ret = info->ops->get_battery_current(info, &val->intval);
		break;

	case POWER_SUPPLY_PROP_TEMP:
		ret = ds278x_get_temp(info, &val->intval);
		break;

	default:
		ret = -EINVAL;
	}

	return ret;
}

static enum power_supply_property ds278x_battery_props[] = {
	POWER_SUPPLY_PROP_STATUS,
	POWER_SUPPLY_PROP_CAPACITY,
	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_CURRENT_NOW,
	POWER_SUPPLY_PROP_TEMP,
};

static void ds278x_power_supply_init(struct power_supply *battery)
{
	battery->type			= POWER_SUPPLY_TYPE_BATTERY;
	battery->properties		= ds278x_battery_props;
	battery->num_properties		= ARRAY_SIZE(ds278x_battery_props);
	battery->get_property		= ds278x_battery_get_property;
	battery->external_power_changed	= NULL;
}

static int ds278x_battery_remove(struct i2c_client *client)
{
	struct ds278x_info *info = i2c_get_clientdata(client);

	power_supply_unregister(&info->battery);
	kfree(info->battery.name);

	mutex_lock(&battery_lock);
	idr_remove(&battery_id, info->id);
	mutex_unlock(&battery_lock);

	kfree(info);
	return 0;
}

enum ds278x_num_id {
	DS2782 = 0,
	DS2786,
};

static struct ds278x_battery_ops ds278x_ops[] = {
	[DS2782] = {
		.get_battery_current  = ds2782_get_current,
		.get_battery_voltage  = ds2782_get_voltage,
		.get_battery_capacity = ds2782_get_capacity,
		.init_reg            = ds2782_init_reg,
	},
	[DS2786] = {
		.get_battery_current  = ds2786_get_current,
		.get_battery_voltage  = ds2786_get_voltage,
		.get_battery_capacity = ds2786_get_capacity,
		.init_reg            = ds2786_init_reg,
	}
};

static int ds278x_battery_probe(struct i2c_client *client,
                                const struct i2c_device_id *id)
{
	struct ds278x_platform_data *pdata = client->dev.platform_data;
	struct ds278x_info *info;
	int ret;
	int num;

	/*
	 * ds2786 should have the sense resistor value set
	 * in the platform data
	 */
	if (id->driver_data == DS2786 && !pdata) {
		dev_err(&client->dev, "missing platform data for ds2786\n");
		return -EINVAL;
	}

	/* Get an ID for this battery */
	ret = idr_pre_get(&battery_id, GFP_KERNEL);
	if (ret == 0) {
		ret = -ENOMEM;
		goto fail_id;
	}

	mutex_lock(&battery_lock);
	ret = idr_get_new(&battery_id, client, &num);
	mutex_unlock(&battery_lock);
	if (ret < 0) {
		goto fail_id;
	}

	info = kzalloc(sizeof(*info), GFP_KERNEL);
	if (!info) {
		ret = -ENOMEM;
		goto fail_info;
	}

	info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num);
	if (!info->battery.name) {
		ret = -ENOMEM;
		goto fail_name;
	}

	if (id->driver_data == DS2786) {
		info->rsns = pdata->rsns;
	}

	i2c_set_clientdata(client, info);
	info->client = client;
	info->id = num;
	info->ops  = &ds278x_ops[id->driver_data];
	ds278x_ops[id->driver_data].init_reg(info);
	ds278x_power_supply_init(&info->battery);

	ret = power_supply_register(&client->dev, &info->battery);
	if (ret) {
		dev_err(&client->dev, "failed to register battery\n");
		goto fail_register;
	}

	return 0;

fail_register:
	kfree(info->battery.name);
fail_name:
	kfree(info);
fail_info:
	mutex_lock(&battery_lock);
	idr_remove(&battery_id, num);
	mutex_unlock(&battery_lock);
fail_id:
	return ret;
}

static const struct i2c_device_id ds278x_id[] = {
	{"ds2782", DS2782},
	{"ds2786", DS2786},
	{},
};

static struct i2c_driver ds278x_battery_driver = {
	.driver 	= {
		.name	= "ds2782-battery",
	},
	.probe		= ds278x_battery_probe,
	.remove		= ds278x_battery_remove,
	.id_table	= ds278x_id,
};

static int __init ds278x_init(void)
{
	return i2c_add_driver(&ds278x_battery_driver);
}
module_init(ds278x_init);

static void __exit ds278x_exit(void)
{
	i2c_del_driver(&ds278x_battery_driver);
}
module_exit(ds278x_exit);

MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
MODULE_LICENSE("GPL");
